package com.artic.base.resourceloader;

/**
 * A default implementation of {@link IClassLoadStrategy} that should be
 * suitable for a variety of situations. See {@link #getClassLoader} for
 * details.
 * 
 * @author aczarnik
 */
public class DefaultClassLoadStrategy implements IClassLoadStrategy {

	private ClassLoader callerLoader;
	private ClassLoader contextLoader = Thread.currentThread()
			.getContextClassLoader();
	private final ClassLoader systemLoader = ClassLoader.getSystemClassLoader();

	public ClassLoader getClassLoader(final ClassLoadContext ctx) {
		if (ctx == null) {
			throw new IllegalArgumentException("null input: ctx");
		}

		callerLoader = ctx.getCallerClass().getClassLoader();

		// if 'callerLoader' and 'contextLoader' are in a parent-child
		// relationship, always choose the child:

		if (isChild(contextLoader, callerLoader)) {
			return callerLoader;
		} else if (isChild(callerLoader, contextLoader)) {
			return contextLoader;
		}

		return systemLoader;
	}

	/**
	 * Returns 'true' if 'loader2' is a delegation child of 'loader1' [or if
	 * 'loader1'=='loader2']. Of course, this works only for classloaders that
	 * set their parent pointers correctly. 'null' is interpreted as the
	 * primordial loader [i.e., everybody's parent].
	 */
	public static boolean isChild(final ClassLoader loader1, ClassLoader loader2) {
		if (loader1 == loader2) {
			return true;
		}
		if (loader2 == null) {
			return false;
		}
		if (loader1 == null) {
			return true;
		}

		for (; loader2 != null; loader2 = loader2.getParent()) {
			if (loader2 == loader1) {
				return true;
			}
		}

		return false;
	}

	public void setCallerLoader(ClassLoader callerLoader) {
		this.callerLoader = callerLoader;
	}

	public void setContextLoader(ClassLoader contextLoader) {
		this.contextLoader = contextLoader;
	}

}